package com.robinmouren.aspect;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.robinmouren.annotation.AutoFill;
import com.robinmouren.constant.IdentityConstant;
import com.robinmouren.constant.MessageConstant;
import com.robinmouren.context.BaseContext;
import com.robinmouren.context.LocalContext;
import com.robinmouren.dto.UserLoginDto;
import com.robinmouren.entity.OperationLog;
import com.robinmouren.entity.User;
import com.robinmouren.enumeration.OperationType;
import com.robinmouren.exception.NullUserInfoException;
import com.robinmouren.service.impl.OperationLogServiceImpl;
import com.robinmouren.service.impl.UserServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;


/**
 * 使用AOP思想对数据中的共享字段进行自动填充
 * 使用了 枚举、AOP、反射、自定义注解
 */
@Aspect
@Component
@Slf4j
public class AutoFillAspect {

    /**
     * 设置切入点
     */
    @Pointcut("execution(* com.robinmouren.controller.*.*(..)) && @annotation(com.robinmouren.annotation.AutoFill)")
    public void autoFillPointCut() {}


    @Autowired
    private UserServiceImpl userService;
    @Autowired
    private OperationLogServiceImpl operationLogService;

    /**
     * 前置通知，在通知中进行公共字段的赋值
     *
     * @param joinPoint
     */
    @Before("autoFillPointCut()")
    public void autoFill(JoinPoint joinPoint) {
        log.info("开始进行公共字段自动填充....");

        // 获取到当前被拦截方法上的数据库操作类型
        // 获取签名对象
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        // 获得签名上的注释对象
        AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class);
        // 获得数据库操作类型
        OperationType operationType = autoFill.value();
        String context = autoFill.context();
        // 获取到被拦截方法参数--实体对象
        Object[] args = joinPoint.getArgs();
        if (args == null || args.length == 0) {
            return;
        }

        Object entity = args[0];
        // 获取操作的用户id
        Long id = BaseContext.getCurrentId();

        // 根据当前不同的操作类型，为对应的属性通过反射来赋值
        if (operationType == OperationType.RECORDING) {

            try {
                // 判断当前方法是否为登录
                if (entity.getClass() == UserLoginDto.class) {
                    String username = ((UserLoginDto) entity).getUsername();
                    User user = userService.getOne(new LambdaQueryWrapper<User>()
                            .eq(User::getUsername, username));
                    // 空指针异常抛出
                    if (user == null) {
                        throw new NullUserInfoException(MessageConstant.USER_NOT_EXIST);
                    }
                    // 覆盖掉id值
                    id = user.getId();
                    // 客户端不用记录
                    if (user.getIdentity().equals(IdentityConstant.CLIENT)) {
                        return;
                    }
                }
                // 向表中添加信息
                OperationLog build = OperationLog.builder()
                        .userId(id)
                        .event(context)
                        .loginIp(LocalContext.fetchLocalIp())
                        .build();
                operationLogService.save(build);


            } catch (Exception e) {
                throw new NullUserInfoException(MessageConstant.USER_NOT_EXIST);
            }
        }

    }


}
